﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Objects;
using System.Globalization;
using System.Linq;
using EstateNuke.Data.Properties;

namespace EstateNuke.Data
{
    /// <summary>
    /// </summary>
    public partial class Agreement
    {
        #region Fileds & Properties

        /// <summary>
        /// </summary>
        public string DisplayName
        {
            get
            {
                return string.Format("{0}{1}", SerialNumber, IsDeleted ? " (已删除)" : "");
            }
        }

        /// <summary>
        /// </summary>
        public Building Building
        {
            get
            {
                if (!RoomReference.IsLoaded)
                {
                    RoomReference.Load(MergeOption.PreserveChanges);
                }
                if (!Room.EntranceReference.IsLoaded)
                {
                    Room.EntranceReference.Load(MergeOption.PreserveChanges);
                }
                if (!Room.Entrance.BuildingReference.IsLoaded)
                {
                    Room.Entrance.BuildingReference.Load(MergeOption.PreserveChanges);
                }
                return Room.Entrance.Building;
            }
        }

        /// <summary>
        /// </summary>
        public Project Project
        {
            get
            {
                if (!RoomReference.IsLoaded)
                {
                    RoomReference.Load(MergeOption.PreserveChanges);
                }
                if (!Room.EntranceReference.IsLoaded)
                {
                    Room.EntranceReference.Load(MergeOption.PreserveChanges);
                }
                if (!Room.Entrance.BuildingReference.IsLoaded)
                {
                    Room.Entrance.BuildingReference.Load(MergeOption.PreserveChanges);
                }
                if (!Room.Entrance.Building.ProjectReference.IsLoaded)
                {
                    Room.Entrance.Building.ProjectReference.Load(MergeOption.PreserveChanges);
                }
                return Room.Entrance.Building.Project;
            }
        }

        /// <summary>
        /// </summary>
        internal IList<SmallOrder> SmallOrders
        {
            get
            {
                return InternalSmallOrders.Where(smallOrder => smallOrder.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public SmallOrder SmallOrder
        {
            get
            {
                return InternalSmallOrders.FirstOrDefault(smallOrder => smallOrder.IsDeleted == false);
            }
        }

        /// <summary>
        /// </summary>
        internal IList<LargeOrder> LargeOrders
        {
            get
            {
                return InternalLargeOrders.Where(largeOrder => largeOrder.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public LargeOrder LargeOrder
        {
            get
            {
                return InternalLargeOrders.FirstOrDefault(largeOrder => largeOrder.IsDeleted == false);
            }
        }

        /// <summary>
        /// </summary>
        internal IList<Contract> Contracts
        {
            get
            {
                return InternalContracts.Where(contract => contract.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public Contract Contract
        {
            get
            {
                return InternalContracts.FirstOrDefault(contract => contract.IsDeleted == false);
            }
        }

        /// <summary>
        /// </summary>
        internal IList<Cancellation> Cancellations
        {
            get
            {
                return InternalCancellations.Where(cancellation => cancellation.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public Cancellation Cancellation
        {
            get
            {
                return InternalCancellations.FirstOrDefault(cancellation => cancellation.IsDeleted == false);
            }
        }

        /// <summary>
        /// </summary>
        internal IList<AgreementDealingReason> AgreementDealingReasons
        {
            get
            {
                return InternalAgreementDealingReasons.Where(agreementDealingReason => agreementDealingReason.IsDeleted == false).ToList();
            }
        }

        /// <summary>
        /// </summary>
        public IList<DealingReason> DealingReasons
        {
            get
            {
                return InternalAgreementDealingReasons.Where(agreementDealingReason => agreementDealingReason.IsDeleted == false && agreementDealingReason.DealingReason.IsDeleted == false).Select(agreementDealingReason => agreementDealingReason.DealingReason).ToList();
            }
        }

        #endregion

        #region Set AgreementState

        /// <summary>
        /// </summary>
        public void SetAgreementStateNone(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            AgreementStateId = 0;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            if (!RoomReference.IsLoaded)
            {
                RoomReference.Load(MergeOption.PreserveChanges);
            }
            if (!Room.ActiveAgreementReference.IsLoaded)
            {
                Room.ActiveAgreementReference.Load(MergeOption.PreserveChanges);
            }
            if (Room.ActiveAgreement == this && (Room.RoomStateId == 1 || Room.RoomStateId == 3 || Room.RoomStateId == 4 || Room.RoomStateId == 5))
            {
                Room.SetRoomStateSellable(modifiedUser);
                Room.ActiveAgreement = null;
            }
        }

        /// <summary>
        /// </summary>
        public void SetAgreementStateSmallOrdered(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            AgreementStateId = 1;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            if (!RoomReference.IsLoaded)
            {
                RoomReference.Load(MergeOption.PreserveChanges);
            }
            if (!Room.ActiveAgreementReference.IsLoaded)
            {
                Room.ActiveAgreementReference.Load(MergeOption.PreserveChanges);
            }
            if (Room.ActiveAgreement == this && (Room.RoomStateId == 1 || Room.RoomStateId == 3 || Room.RoomStateId == 4 || Room.RoomStateId == 5))
            {
                Room.SetRoomStateSmallOrdered(modifiedUser);
            }
        }

        /// <summary>
        /// </summary>
        public void SetAgreementStateLargeOrdered(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            AgreementStateId = 2;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            if (!RoomReference.IsLoaded)
            {
                RoomReference.Load(MergeOption.PreserveChanges);
            }
            if (!Room.ActiveAgreementReference.IsLoaded)
            {
                Room.ActiveAgreementReference.Load(MergeOption.PreserveChanges);
            }
            if (Room.ActiveAgreement == this && (Room.RoomStateId == 1 || Room.RoomStateId == 3 || Room.RoomStateId == 4 || Room.RoomStateId == 5))
            {
                Room.SetRoomStateLargeOrdered(modifiedUser);
            }
            var smallOrder = SmallOrder;
            if (smallOrder != null)
            {
                (smallOrder.InternalSmallOrderExpiredAlerts.ToList()).ForEach(smallOrderExpiredAlert => smallOrderExpiredAlert.Delete(modifiedUser));
            }
        }

        /// <summary>
        /// </summary>
        public void SetAgreementStateContracted(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            AgreementStateId = 4;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            if (!RoomReference.IsLoaded)
            {
                RoomReference.Load(MergeOption.PreserveChanges);
            }
            if (!Room.ActiveAgreementReference.IsLoaded)
            {
                Room.ActiveAgreementReference.Load(MergeOption.PreserveChanges);
            }
            if (Room.ActiveAgreement == this && (Room.RoomStateId == 1 || Room.RoomStateId == 3 || Room.RoomStateId == 4 || Room.RoomStateId == 5))
            {
                Room.SetRoomStateContracted(modifiedUser);
            }
            var smallOrder = SmallOrder;
            if (smallOrder != null)
            {
                (smallOrder.InternalSmallOrderExpiredAlerts.ToList()).ForEach(smallOrderExpiredAlert => smallOrderExpiredAlert.Delete(modifiedUser));
            }
            var largeOrder = LargeOrder;
            if (largeOrder != null)
            {
                (largeOrder.InternalLargeOrderEngagedContractAlerts.ToList()).ForEach(largeOrderEngagedContractAlert => largeOrderEngagedContractAlert.Delete(modifiedUser));
            }
        }

        /// <summary>
        /// </summary>
        public void SetAgreementStateCancelled(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            AgreementStateId = 8;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            if (!RoomReference.IsLoaded)
            {
                RoomReference.Load(MergeOption.PreserveChanges);
            }
            if (!Room.ActiveAgreementReference.IsLoaded)
            {
                Room.ActiveAgreementReference.Load(MergeOption.PreserveChanges);
            }
            if (Room.ActiveAgreement == this && (Room.RoomStateId == 1 || Room.RoomStateId == 3 || Room.RoomStateId == 4 || Room.RoomStateId == 5))
            {
                Room.SetRoomStateSellable(modifiedUser);
                Room.ActiveAgreement = null;
            }
            var smallOrder = SmallOrder;
            if (smallOrder != null)
            {
                (smallOrder.InternalSmallOrderExpiredAlerts.ToList()).ForEach(smallOrderExpiredAlert => smallOrderExpiredAlert.Delete(modifiedUser));
            }
            var largeOrder = LargeOrder;
            if (largeOrder != null)
            {
                (largeOrder.InternalLargeOrderEngagedContractAlerts.ToList()).ForEach(largeOrderEngagedContractAlert => largeOrderEngagedContractAlert.Delete(modifiedUser));
            }
            var contract = Contract;
            if (contract != null)
            {
                (contract.InternalContractReceipts.SelectMany(contractReceipt => contractReceipt.InternalContractReceiptScheduledAlerts).ToList()).ForEach(contractReceiptScheduledAlert => contractReceiptScheduledAlert.Delete(modifiedUser));
            }
        }

        /// <summary>
        /// </summary>
        private void AutoSetAgreementState(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (Cancellation != null)
            {
                SetAgreementStateCancelled(modifiedUser);
            }
            else if (Contract != null)
            {
                SetAgreementStateContracted(modifiedUser);
            }
            else if (LargeOrder != null)
            {
                SetAgreementStateLargeOrdered(modifiedUser);
            }
            else if (SmallOrder != null)
            {
                SetAgreementStateSmallOrdered(modifiedUser);
            }
            else
            {
                SetAgreementStateNone(modifiedUser);
            }
        }

        #endregion

        #region Create & Delete SmallOrder

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public SmallOrder CreateSmallOrder(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (LargeOrder != null || Contract != null || Cancellation != null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.CannotCreateSmallOrder, "已存在大定、已签或取消协议"));
            }
            SetAgreementStateSmallOrdered(modifiedUser);
            var smallOrder = InternalSmallOrders.FirstOrDefault(item => item.IsDeleted == false);
            if (smallOrder != null)
            {
                return smallOrder;
            }
            smallOrder = InternalSmallOrders.FirstOrDefault(item => item.IsDeleted);
            if (smallOrder == null)
            {
                smallOrder = new SmallOrder
                                 {
                                     RowId = Guid.NewGuid(),
                                     CreatedDate = DateTime.Now,
                                 };
                InternalSmallOrders.Add(smallOrder);
            }
            smallOrder.IsDeleted = false;
            smallOrder.ModifiedDate = DateTime.Now;
            smallOrder.ModifiedByUser = modifiedUser;
            smallOrder.OrderNumber = string.Format("XD{0}{1}{2}", Project.CreatedDate.Year, Project.Code, (Project.AllSmallOrdersCount + 1).ToString("00000"));
            smallOrder.ReducedUnitPrice = Room.UnitPrice;
            smallOrder.ReducedTotalPrice = Room.TotalPrice;
            smallOrder.DiscountRate = 100;
            smallOrder.PaymentTypeId = 1;
            smallOrder.TotalAmount = Room.TotalPrice;
            smallOrder.DepositAmount = 1000;
            smallOrder.FirstInstalmentAmount = 0;
            smallOrder.SignatureDate = DateTime.Now.Date;
            smallOrder.ExpiredDate = DateTime.Now.Date.AddDays(7);
            smallOrder.EndAutomatically = true;
            return smallOrder;
        }

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void DeleteSmallOrder(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var smallOrder = SmallOrder;
            if (smallOrder != null)
            {
                smallOrder.Delete(modifiedUser);
                AutoSetAgreementState(modifiedUser);
            }
        }

        #endregion

        #region Create & Delete LargeOrder

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public LargeOrder CreateLargeOrder(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (Contract != null || Cancellation != null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.CannotCreateSmallOrder, "已存在已签或取消协议"));
            }
            SetAgreementStateLargeOrdered(modifiedUser);
            var largeOrder = InternalLargeOrders.FirstOrDefault(item => item.IsDeleted == false);
            if (largeOrder != null)
            {
                return largeOrder;
            }
            largeOrder = InternalLargeOrders.FirstOrDefault(item => item.IsDeleted);
            if (largeOrder == null)
            {
                largeOrder = new LargeOrder
                                 {
                                     RowId = Guid.NewGuid(),
                                     CreatedDate = DateTime.Now,
                                 };
                InternalLargeOrders.Add(largeOrder);
            }
            largeOrder.IsDeleted = false;
            largeOrder.ModifiedDate = DateTime.Now;
            largeOrder.ModifiedByUser = modifiedUser;
            largeOrder.OrderNumber = string.Format("DD{0}{1}{2}", Project.CreatedDate.Year, Project.Code, (Project.AllLargeOrdersCount + 1).ToString("00000"));
            var smallOrder = SmallOrder;
            largeOrder.ReducedUnitPrice = smallOrder != null ? smallOrder.ReducedUnitPrice : Room.UnitPrice;
            largeOrder.ReducedTotalPrice = smallOrder != null ? smallOrder.ReducedTotalPrice : Room.TotalPrice;
            largeOrder.DiscountRate = smallOrder != null ? smallOrder.DiscountRate : 100;
            largeOrder.PaymentTypeId = smallOrder != null ? smallOrder.PaymentTypeId : 1;
            largeOrder.TotalAmount = smallOrder != null ? smallOrder.TotalAmount : Room.TotalPrice;
            largeOrder.DepositAmount = 10000;
            largeOrder.FirstInstalmentAmount = smallOrder != null ? smallOrder.FirstInstalmentAmount : 0;
            largeOrder.MortgageAmount = 0;
            largeOrder.EngagedContractDate = DateTime.Now.Date.AddDays(7);
            largeOrder.SignatureDate = DateTime.Now.Date;
            return largeOrder;
        }

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void DeleteLargeOrder(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var largeOrder = LargeOrder;
            if (largeOrder != null)
            {
                largeOrder.Delete(modifiedUser);
                AutoSetAgreementState(modifiedUser);
            }
        }

        #endregion

        #region Create & Delete Contract

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public Contract CreateContract(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (Cancellation != null)
            {
                throw new InvalidOperationException(string.Format(CultureInfo.CurrentCulture, Resources.CannotCreateSmallOrder, "已存在取消协议"));
            }
            SetAgreementStateContracted(modifiedUser);
            var contract = InternalContracts.FirstOrDefault(item => item.IsDeleted == false);
            if (contract != null)
            {
                return contract;
            }
            contract = InternalContracts.FirstOrDefault(item => item.IsDeleted);
            if (contract == null)
            {
                contract = new Contract
                               {
                                   RowId = Guid.NewGuid(),
                                   CreatedDate = DateTime.Now,
                               };
                InternalContracts.Add(contract);
            }
            contract.IsDeleted = false;
            contract.ModifiedDate = DateTime.Now;
            contract.ModifiedByUser = modifiedUser;
            var largeOrder = LargeOrder;
            var smallOrder = SmallOrder;
            contract.ContractNumber = string.Format("HT{0}{1}{2}", Project.CreatedDate.Year, Project.Code, (Project.AllContractsCount + 1).ToString("00000"));
            contract.ReducedUnitPrice = largeOrder != null ? largeOrder.ReducedUnitPrice : (smallOrder != null ? smallOrder.ReducedUnitPrice : Room.UnitPrice);
            contract.ReducedTotalPrice = largeOrder != null ? largeOrder.ReducedTotalPrice : (smallOrder != null ? smallOrder.ReducedTotalPrice : Room.TotalPrice);
            contract.DiscountRate = largeOrder != null ? largeOrder.DiscountRate : (smallOrder != null ? smallOrder.DiscountRate : 100);
            contract.PaymentTypeId = largeOrder != null ? largeOrder.PaymentTypeId : (smallOrder != null ? smallOrder.PaymentTypeId : 1);
            contract.TotalAmount = largeOrder != null ? largeOrder.TotalAmount : (smallOrder != null ? smallOrder.TotalAmount : Room.TotalPrice);
            contract.FirstInstalmentAmount = largeOrder != null ? largeOrder.FirstInstalmentAmount : (smallOrder != null ? smallOrder.FirstInstalmentAmount : 0);
            contract.MortgageAmount = largeOrder != null ? largeOrder.MortgageAmount : 0;
            contract.SignatureDate = DateTime.Now.Date;
            return contract;
        }

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void DeleteContract(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var contract = Contract;
            if (contract != null)
            {
                contract.Delete(modifiedUser);
                AutoSetAgreementState(modifiedUser);
            }
        }

        #endregion

        #region Create & Delete Cancellation

        /// <summary>
        /// </summary>
        /// <returns></returns>
        public Cancellation CreateCancellation(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            SetAgreementStateCancelled(modifiedUser);
            var cancellation = InternalCancellations.FirstOrDefault(item => item.IsDeleted == false);
            if (cancellation != null)
            {
                return cancellation;
            }
            cancellation = InternalCancellations.FirstOrDefault(item => item.IsDeleted);
            if (cancellation == null)
            {
                cancellation = new Cancellation
                                   {
                                       RowId = Guid.NewGuid(),
                                       CreatedDate = DateTime.Now,
                                   };
                InternalCancellations.Add(cancellation);
            }
            cancellation.IsDeleted = false;
            cancellation.ModifiedDate = DateTime.Now;
            cancellation.ModifiedByUser = modifiedUser;
            cancellation.DeductibleAmount = 0;
            cancellation.CancellationDate = DateTime.Now;
            return cancellation;
        }

        /// <summary>
        /// </summary>
        /// <param name = "modifiedUser"></param>
        public void DeleteCancellation(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var cancellation = Cancellation;
            if (cancellation != null)
            {
                cancellation.Delete(modifiedUser);
                AutoSetAgreementState(modifiedUser);
            }
        }

        #endregion

        #region Add & Remove AgreementDealingReason

        /// <summary>
        /// </summary>
        /// <returns></returns>
        internal AgreementDealingReason AddAgreementDealingReason(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            var agreementDealingReason = new AgreementDealingReason
                                             {
                                                 RowId = Guid.NewGuid(),
                                                 CreatedDate = DateTime.Now,
                                             };
            InternalAgreementDealingReasons.Add(agreementDealingReason);
            agreementDealingReason.IsDeleted = false;
            agreementDealingReason.ModifiedDate = DateTime.Now;
            agreementDealingReason.ModifiedByUser = modifiedUser;
            return agreementDealingReason;
        }

        /// <summary>
        /// </summary>
        /// <param name = "agreementDealingReason"></param>
        /// <param name = "modifiedUser"></param>
        internal void RemoveAgreementDealingReason(AgreementDealingReason agreementDealingReason, User modifiedUser)
        {
            if (agreementDealingReason == null)
            {
                throw new ArgumentNullException("agreementDealingReason");
            }
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (InternalAgreementDealingReasons.Contains(agreementDealingReason))
            {
                agreementDealingReason.Delete(modifiedUser);
            }
        }

        #endregion

        #region Set DealingReasons

        /// <summary>
        /// </summary>
        /// <param name = "dealingReasons"></param>
        /// <param name = "modifiedUser"></param>
        public void SetDealingReasons(IList<DealingReason> dealingReasons, User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            if (dealingReasons != null)
            {
                var agreementDealingReasons = InternalAgreementDealingReasons.ToList();
                agreementDealingReasons.ForEach(agreementDealingReason => agreementDealingReason.Delete(modifiedUser));
                foreach (var dealingReason in dealingReasons.Distinct())
                {
                    var currentAgreementDealingReason = agreementDealingReasons.FirstOrDefault(agreementDealingReason => agreementDealingReason.DealingReason == dealingReason);
                    if (currentAgreementDealingReason != null)
                    {
                        currentAgreementDealingReason.CancelDelete(modifiedUser);
                    }
                    else
                    {
                        currentAgreementDealingReason = AddAgreementDealingReason(modifiedUser);
                        currentAgreementDealingReason.DealingReason = dealingReason;
                    }
                }
            }
        }

        #endregion

        #region Ensure

        /// <summary>
        /// </summary>
        public void Ensure()
        {
            if (string.IsNullOrEmpty(SerialNumber))
            {
                throw new EntityException(string.Format(CultureInfo.CurrentCulture, Resources.NotNullAllowed, RowId, "Agreement", "SerialNumber"));
            }
            SerialNumber = SerialNumber.Length > 32 ? SerialNumber.Substring(0, 32) : SerialNumber;
        }

        #endregion

        #region Delete

        /// <summary>
        /// </summary>
        public void Delete(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            IsDeleted = true;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
            ((List<AgreementDealingReason>) AgreementDealingReasons).ForEach(agreementDealingReason => agreementDealingReason.Delete(modifiedUser));
            ((List<SmallOrder>) SmallOrders).ForEach(smallOrder => smallOrder.Delete(modifiedUser));
            ((List<LargeOrder>) LargeOrders).ForEach(largeOrder => largeOrder.Delete(modifiedUser));
            ((List<Contract>) Contracts).ForEach(contract => contract.Delete(modifiedUser));
            ((List<Cancellation>) Cancellations).ForEach(cancellation => cancellation.Delete(modifiedUser));
        }

        /// <summary>
        /// </summary>
        public void CancelDelete(User modifiedUser)
        {
            if (modifiedUser == null)
            {
                throw new ArgumentNullException("modifiedUser");
            }
            IsDeleted = false;
            ModifiedDate = DateTime.Now;
            ModifiedByUser = modifiedUser;
        }

        #endregion
    }
}